//------------------------------
// Webhoover v20130422
// Juha Kylmänen, OUSPG
// jutaky@ee.oulu.fi
// https://www.ee.oulu.fi/research/ouspg/
// Licensed under MIT license
//------------------------------

var app = require('http').createServer(handler);
var io = require('socket.io').listen(app);
var fs = require('fs');
var pty = require('pty.js');
var path = require('path');
var conf = require("./conf.json");

app.listen(conf.listen_port, conf.listen_host);

function handler (req, res) {

    console.log("Request for "+req.url);

    //------------------------------
    // Serve the index.html file
    //------------------------------
    if (req.url == "/") {   
        fs.readFile(__dirname + '/public_html/index.html',
        function (err, data) {
            if (err) {
                res.writeHead(500);
                return res.end('Error loading index.html');
            }

            res.writeHead(200);
            res.end(data);
        });
        return;
    }

    //------------------------------
    // Other files
    //------------------------------
    var requested_file = path.normalize(__dirname + '/public_html/' +req.url);
    var prefix = __dirname + '/public_html/';

    if (requested_file.substring(0, prefix.length) == prefix) {
        fs.readFile(requested_file,
            function (err, data) {
            if (err) {
                res.writeHead(500);
                return res.end('Error loading '+req.url);
            }

            res.writeHead(200);
            res.end(data);
                
          });
    } else {
        res.writeHead(403);
        return res.end('Permission denied: '+req.url);
    }
}

var spawned = false;
var spawn;
var hoover;

//------------------------------
// Handling of incoming connections
//------------------------------
io.sockets.on('connection', function (socket) {
   
    //------------------------------
    // Listening the command channel
    //------------------------------
    socket.on('command', function (data) {
        console.log("Received command: "+data.msg);

        //------------------------------
        // Fetching list of interfaces,
        // Node.js not returning all interfaces? Skipping ones without IP?
        // --> parsing hoover's detection
        //------------------------------
        if (data.msg == "interfaces") {
            console.log("Interfaces query received");

            hoover = pty.spawn(conf.python_bin, [conf.hoover_py, '--list']);

            hoover.on('data', function (data) {
                console.log(data);

                var text = data.toString();
                var lines = text.split('\n');
                
                for (var i in lines) {
                    if (lines[i].search("Interface name: ") != 0) {
                        continue;
                    }
                    var tmp = lines[i].replace("Interface name: ","");
                    tmp = tmp.replace("\n","");
                    tmp = tmp.replace("\r","");
                    socket.emit('interfaces', { interfaces: tmp });
                }
            });

            return;
        }

        //------------------------------
        // Fetching list of fuzzers with a hack'ish parser
        //------------------------------
        if (data.msg == "fuzzers") {
            console.log("Fuzzers query received");

            hoover = pty.spawn(conf.python_bin, [conf.hoover_py, '--fuzzers']);

            hoover.on('data', function (data) {
                console.log(data);

                var text = data.toString();
                var lines = text.split('\033[1m');
                
                for (var i in lines) {
                    if (lines[i].search(": ") == -1) {
                        continue;
                    }

                    var tmp = lines[i].split("\033[0;0m");
                    socket.emit('fuzzers', { fuzzers: tmp[0] });
                }
            });

            return;
        }

        //------------------------------
        // Hoover starting and stopping commands
        //------------------------------
        if (data.msg == "hoover_start" && spawned == true) {
            console.log("Error: Hoover already running, skipping command");
            socket.emit('toclient', { clientlog: "Hoover already running!" });
            return;
        }
        
        if (data.msg == "hoover_stop" && spawned == true) {
            console.log("Killing the hoover!");
            spawned = false;
            hoover.write('q\r');
            //------------------------------
            // Kill the process, to make sure the hoover exits if q fails
            //------------------------------
            setTimeout((function() {
                hoover.kill();
            }), 2500);
            
            return;
        }
        
        if (data.msg == "hoover_stop" && spawned == false) {
            console.log("Error: Hoover is not running, skipping command");
            socket.emit('toclient', { clientlog: "Hoover is not running!" });
            return;
        }

        //------------------------------
        // OK to spawn the hoover
        //------------------------------
        if (data.msg == "hoover_start" && spawned == false) {
            console.log("Launching hoover...");
            spawned = true;

            //-----------------------------------
            // Compile list of arguments for the hoover
            //-----------------------------------
            var hoover_args = [];

            hoover_args.push(conf.hoover_py);

            hoover_args.push('-i');
            hoover_args.push(data.inputif);
            hoover_args.push(data.outputif);

            if (data.proxy == 1) {
                hoover_args.push('--proxy');
            }

            //-----------------------------------
            // Fuzzer(s) setup
            //-----------------------------------
            if (data.fuzzers.length > 0 && data.fuzzing_enabled == 1) {
                hoover_args.push('--fuzz');

                for (var i in data.fuzzers) {
                    hoover_args.push(data.fuzzers[i]);
                }

                if (!(data.fuzzers.length == 1 && data.fuzzers[0] == 'Duplicate')) {
                    hoover_args.push('--fuzzing-probability');
                    hoover_args.push(data.fprobability);
                }

                if (data.flevel != 10) {
                    hoover_args.push('--level');
                    hoover_args.push(data.flevel);
                }

                if (data.ftimer > 0) {
                    hoover_args.push('--timer');
                    hoover_args.push(data.ftimer);
                }

                if (data.fuzzers.indexOf("Duplicate") != -1) {  
                    hoover_args.push('--duplicate-probability');
                    hoover_args.push(data.duplicate);
                }
            }

            //-----------------------------------
            // Drop setup
            //-----------------------------------
            if (data.drops.length > 0) {
                hoover_args.push('--drop');

                for (var i in data.drops) {
                    hoover_args.push(data.drops[i]);
                }
            }

            //-----------------------------------
            // Logging setup
            //-----------------------------------
            if (data.loutput.length > 0) {
                hoover_args.push('--log');
                hoover_args.push(data.loutput);

                hoover_args.push(data.lformat);

                if (data.lformat == 'hoover' && data.lverbose == 1) {
                    hoover_args.push('true');
                }

                if (data.lformat == 'pcap' && data.lunaltered == 1) {
                    hoover_args.push('--log-unaltered');
                }
            }

            //-----------------------------------
            // Packet filtering setup
            //-----------------------------------
            if (data.flt_ipsrc.length > 0 || data.flt_ipdst.length > 0 ||
                data.flt_srcport.length > 0 || data.flt_dstport.length > 0 ||
                (data.flt_protocol.length > 0 && data.flt_protocol != 'none')) {
                
                hoover_args.push('--filter');
            }

            if (data.flt_ipsrc.length > 0) {
                
                hoover_args.push('ipsrc');
                hoover_args.push(data.flt_ipsrc);
            }

            if (data.flt_ipdst.length > 0) {
                
                hoover_args.push('ipdst');
                hoover_args.push(data.flt_ipdst);
            }

            if (data.flt_srcport.length > 0) {
                
                hoover_args.push('srcport');
                hoover_args.push(data.flt_srcport);
            }

            if (data.flt_dstport.length > 0) {
                
                hoover_args.push('dstport');
                hoover_args.push(data.flt_dstport);
            }

            if (data.flt_protocol.length > 0 && data.flt_protocol != 'none') {
                
                hoover_args.push('proto');
                hoover_args.push(data.flt_protocol);
            }

            //-----------------------------------
            // Spoofing setup
            //-----------------------------------
            if (data.s_ipsrc.length > 0 || data.s_ipdst.length > 0 ||
                data.s_ip6src.length > 0 || data.s_ip6dst.length > 0 ||
                data.s_ethsrc.length > 0 || data.s_ethdst.length > 0) {
                
                hoover_args.push('--spoofing');
            }

            if (data.s_ipsrc.length > 0) {
                
                hoover_args.push('ipsrc');
                hoover_args.push(data.s_ipsrc);
            }

            if (data.s_ipdst.length > 0) {
                
                hoover_args.push('ipdst');
                hoover_args.push(data.s_ipdst);
            }

            if (data.s_ip6src.length > 0) {
                
                hoover_args.push('ip6src');
                hoover_args.push(data.s_ip6src);
            }

            if (data.s_ip6dst.length > 0) {
                
                hoover_args.push('ip6dst');
                hoover_args.push(data.s_ip6dst);
            }

            if (data.s_ethsrc.length > 0) {
                
                hoover_args.push('ethsrc');
                hoover_args.push(data.s_ethsrc);
            }

            if (data.s_ethdst.length > 0) {
                
                hoover_args.push('ethdst');
                hoover_args.push(data.s_ethdst);
            }

            //-----------------------------------
            // Additional fuzzing options
            //-----------------------------------
            if (data.fuzzing_options.length > 0) {
                
                hoover_args.push('--fuzzing-options');
                var tmp = data.fuzzing_options.split(" ");

                for (i in tmp) {
                    hoover_args.push(tmp[i]);
                }
            }

            //-----------------------------------
            // Replay? (overrides arguments)
            //-----------------------------------
            if (data.replay == 1) {
                hoover_args = [];

                hoover_args.push(conf.hoover_py);

                hoover_args.push('--replay');
                hoover_args.push(data.replay_from);
                
                hoover_args.push(data.outputif);

                if (data.replay_interval.length > 0 && data.replay_interval > 0) {
                    hoover_args.push(data.replay_interval);
                }
            }

            //-----------------------------------
            // From settings file? (also overrides arguments)
            //-----------------------------------
            if (data.settings_from_file.length > 0) {
                hoover_args = [];

                hoover_args.push(conf.hoover_py);

                hoover_args.push('--settings');
                hoover_args.push(data.settings_from_file);
            }

            //-----------------------------------
            // Launch the hoover!
            //-----------------------------------
            console.log("executing: "+conf.python_bin+" "+ hoover_args.join(" "));
            socket.emit("toclient", {clientlog: "executing: "+conf.python_bin+" "+ hoover_args.join(" ")});
            hoover = pty.spawn(conf.python_bin, hoover_args);

            //-----------------------------------
            // Send stdout and stderr of hoover
            // to console and to the web client
            //-----------------------------------
            hoover.on('data', function (data) {
                console.log(data);

                //-------------------------------
                // Sniff error messages
                //-------------------------------
                if ( data.toString().search('Error while initializing hoover: ') != -1 ) {
                    var tmp = data.toString().replace("Type '-h' or '--help' for more information.","");
                    socket.emit('error', { msg: tmp });
                    return;
                } 

                //-------------------------------
                // Sniff logging messages
                //-------------------------------
                if ( data.toString().search('Defined logfile already exists. Pcap-format doesn\'t support files with multiple sessions. Do you want to overwrite file') != -1 ||
                     data.toString().search('Defined logfile already exists. Do you want to add all the new logging information to the end of the defined file?') != -1) {
                    var tmp = data.toString().replace("[y/n]:","");
                    socket.emit('log_output_msg', { msg: tmp });
                    return;
                }

                socket.emit('hoover_output', { clientlog: data.toString() });
            });

            hoover.on('close', function (code) {
                console.log('child process exited with code ' + code);
                socket.emit('hoover_shutdown', { clientlog: "Hoover shutdown" });
                spawned = false;
            });

            return;
        }

        //------------------------------
        // Send unidentified command to pty as is
        //------------------------------
        if (spawned == true) {
            hoover.write(data.msg+'\r');
        } else {
            socket.emit('toclient', { clientlog: "Hoover not running!" });  
        }
    });

    //------------------------------
    // Client lost
    //------------------------------
    socket.on('disconnect', function (socket) {

        if (spawned == true) {
            
            console.log("Client lost and disconnected while hoover spawned, killing hoover");
            hoover.write('q\r');
            //------------------------------
            // Kill the process, to make sure the hoover exits if q fails
            //------------------------------
            setTimeout((function() {
                hoover.kill();
            }), 1500);
        }
    });
});


